package org.hackystat.devcathlon.engine.team;

import java.io.File;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.hackystat.devcathlon.engine.project.ProjectManager;
import org.hackystat.devcathlon.engine.project.TeamProject;
import org.hackystat.devcathlon.engine.user.User;
import org.hackystat.devcathlon.engine.user.UserManager;
import org.hackystat.sensorbase.resource.projects.jaxb.Project;
import org.hackystat.utilities.logger.HackystatLogger;

/**
 * Provides information about a Devcathlon team. 
 * The team name should be a unique ID for this team. 
 * Clients create instances of Teams through the TeamManager class. 
 * @author Philip Johnson
 */
public class Team implements Serializable {
  /** Support serialization. */ 
  private static final long serialVersionUID = 1L;
  /** Primary key reference id. */
  private Long id;
  /** The (unique) name of this team. */  
  private String name;
  /** The owner of this team. */
  private User owner;
  /** The set of members who accepted the invite. */
  private Set<User> members = new HashSet<User>();
  /** The set of members who were invited.*/
  private Set<User> invited = new HashSet<User>();
  /** The map that shows which members accepted their invite. */
  private Map<String, String> memberStatus = new HashMap<String, String>();
  /** The project associated with this team. */
  private String projectName = "";
  /** The description of the team. */
  private String description = "";
  /** The project associated with this team. */
  private Project project;
  /** The mapped project for this team. */
  private TeamProject teamProject;
  /** The name of the team logo file. */  
  private String teamLogoPath = 
    System.getProperty("user.home") + "/.hackystat/devcathlon/avatars/team/defaultlogo.png";

  /**
   * Default Team constructor.
   */
  public Team() {
    // empty constructor
  }

  /**
   * Creates a new team.  Teams are created through the TeamManager. 
   * @param name The (unique) name of the team.
   * @param owner The user who owns this team.  
   * @param members The set of members on this team. 
   */
  Team(String name, User owner, Set<User> members)  {
    this.name = name;
    this.owner = owner;
    this.members = members;
    
    this.memberStatus.put(owner.getEmail(), "accepted");
  }

  /**
   * Get the id of this Team.
   * @return the team id
   */
  public Long getId() {
    return id;
  }

  /**
   * Set the id of this Team.
   * @param id the team id
   */
  public void setId(Long id) {
    this.id = id;
  }

  /**
   * Set the team name.
   * @param name the team name
   */
  public void setName(String name) {
    this.name = name;
  }
  
  /**
   * Returns the name of this team. 
   * @return The name. 
   */
  public String getName() {
    return this.name;
  }

  /**
   * Sets the set of team members.
   * @param members The set of members
   */
  public void setMembers(Set<User> members) {
    this.members = members;
  }

  /**
   * Returns the users who are members of this team. 
   * @return The users. 
   */
  public Set<User> getMembers() {
    return this.members;
  }
  
  /**
   * Sets the set of invited users.
   * @param invited The invited users.
   */
  public void setInvited(Set<User> invited) {
    this.invited = invited;
  }

  /**
   * Returns the users who are invited to the team.
   * @return The users.
   */
  public Set<User> getInvited() {
    return this.invited;
  }

  /**
   * Sets the team owner.
   * @param owner The team owner.
   */
  public void setOwner(User owner) {
    this.owner = owner;
  }

  /**
   * Returns the owner of this team. 
   * @return The team owner. 
   */
  public User getOwner() {
    return this.owner;
  }
  
  /**
   * Returns the project associated with this team.
   * @return The project name. 
   */
  public String getProjectName() {
    return this.projectName;
  }
    
  /**
   * Returns the description associated with the team.
   * @return The description.
   */
  public String getDescription() {
    return this.description;
  }
  
  /**
   * Gets the team logo file path for the team.
   * The image is expected to be found in .hackystat/devcathlon/avatars/team/ directory.   
   * @return The file path to the logo.
   */
  public String getTeamLogoPath() {
    return this.teamLogoPath; 
  }
  
  /**
   * Sets the name of the team. 
   * @param teamName The team name.
   */
  public void setTeamName(String teamName) {
    this.name = teamName;
  }
  
  /**
   * Sets the description for the provided team.
   * @param description The description.
   */
  public void setDescription(String description) {
    this.description = description;
  }
  
  /**
   * Sets the project name associated with this team.
   * @param projectName The project name.
   */
  public void setProjectName(String projectName) {
    this.projectName = projectName;
  }
  
  /**
   * Sets the team logo for the team.
   * The image is expected to be found in .hackystat/devcathlon/avatars/team/ directory.
   * @param filename The file name of image.   
   */
  public void setTeamLogoPath(String filename) {    
    String logoPath = System.getProperty("user.home") + "/.hackystat/devcathlon/avatars/team/";
    logoPath =  logoPath + filename;
    File file = new File(logoPath);      
    if (file.exists()) { 
      this.teamLogoPath = logoPath;
    }
    else {
      HackystatLogger.getLogger("org.hackystat.devcathlon", "devcathlon")
        .info("Team logo could not be found in " + logoPath);
      HackystatLogger.getLogger("org.hackystat.devcathlon", "devcathlon")
        .info("Using default logo for team, " + this.name);
    }
    
  }
  
  /** 
   * Create team invites list for this team.
   * @param invitedUsers The people to invite.
   */
  public void sendInvites(Set<User> invitedUsers) {
    this.invited = invitedUsers;
  }
  
  /**
   *  Adds the member to the member status map as accepted.
   *  Also requires password to update credentials in case generic were
   *  used by default.
   *  @param email The member who accepted the invitation.
   *  @param password The member's password. 
   */
  public void acceptTeamInvite(String email, String password) {
    this.memberStatus.put(email, "accepted");
    
    // In the event, that user was added with generic credentials.
    if (UserManager.getInstance().getUser(email).getPassword().equals(email)) {
      UserManager.getInstance().getUser(email).updateCredentials(email, password);
    }
    // Add to official list of members.
    this.members.add(UserManager.getInstance().getUser(email));
    this.invited.remove(UserManager.getInstance().getUser(email));
  }
  
  /**
   * Adds the member to the member status map as declined.
   * @param email The member who declined the invitation.
   */
  public void declineTeamInvite(String email) {
//    this.members.remove(UserManager.getInstance().getUser(email));
    this.memberStatus.put(email, "declined");
    this.invited.remove(UserManager.getInstance().getUser(email));
  }
  
  /**
   * Removes the user from the team.  Triggered by resigning.
   * @param email The user's email.
   */
  public void resignFromTeam(String email) {
    this.members.remove(UserManager.getInstance().getUser(email));
    this.memberStatus.remove(email);
    this.memberStatus.put(email, "resigned");
  }
  
  /**
   * Returns the member status for this team. 
   * @param email The member who we are looking into.
   * @return The status: "accepted", "declined" or null if none.
   */
  public String getMemberStatus(String email) {
    return this.memberStatus.get(email);
  }

  /**
   * Sets the set of member statuses.
   * @param memberStatus The set of member statuses.
   */
  public void setMemberStatus(Map<String, String> memberStatus) {
    this.memberStatus = memberStatus;
  }

  /**
   * Returns the Map of member statuses for this team.
   * @return The Map of member statuses.
   */
  public Map<String, String> getMemberStatus() {
    return this.memberStatus;
  }
  
  /**
   * Returns if the user officially accepted 
   * invitation and is part of team.
   * @param email The member who we are looking into.
   * @return True if the member is part of team.
   */
  public boolean isMember(String email) {
    return this.members.contains(UserManager.getInstance().getUser(email));
  }
  
  /**
   * Accept all invites for all members.  
   * Used for initializing data.
   */
  public void acceptAllMemberInvites() {
    for (User user : this.invited) {
      this.members.add(user);
    }
    this.invited.clear();
  }
  
  /**
   * Sets the project for this team.
   * @param project The project.
   */
  public void setProject(Project project) {
    String name = project.getName();
    String owner = project.getOwner();
    this.setProjectName(name);
    this.setTeamProject(ProjectManager.getInstance().addProject(this, name, owner));
    this.project = project;
  }
  
  /**
   * Sets the team project.
   * @param project The team project.
   */
  public void setTeamProject(TeamProject project) {
    this.teamProject = project;
  }
  
  /**
   * Gets the project for this team.
   * @return The project.
   */
  public Project getProject() {
    TeamProject teamProject = null;
    try {
      teamProject = this.getTeamProject();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    if (null == teamProject) {
      return null;
    }
    String name = teamProject.getName();
    String owner = teamProject.getOwner();
    try {
      this.project = ProjectManager.getInstance().getProject(name, owner);
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    return this.project;
  }
  
  /**
   * Gets the team project.
   * @return The team project.
   * @throws Exception when an error occurs.
   */
  public TeamProject getTeamProject() throws Exception {
    return this.teamProject;
  }
}
